﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using TimeMachine.Engine.Analytics;

namespace TimeMachine.Engine.Charting.Google
{
	public class GooglePlotter : IPlotter
	{
		public string TryGetPlotUrl(PlotData data)
		{
			PlotPoints[] ps = data.Points;
			if(ps.Length == 0) return null;
			double[][] ys =
				Enumerable.Range(0, ps.First().Values.Length)
				.Select(i => ps.Select(p => p.Values[i])
					.ToArray()).ToArray();
			int width = Math.Min(800, data.PreferedWidth);
			int height = width * 375 / 800;
			return BuildUrl(GetXLables(ps), GetYLabels(ys), ys, width, height);
		}

		public int GetPreferedPointsCount()
		{
			return 25;
		}

		private static double[] GetYLabels(double[][] ps)
		{
			double height = ps.Max(p => p.Max());
			if(height == 0) height = 1;
			double yStepSize = GetYGridStep(height);
			var yStepsCount = (int)Math.Ceiling(height / yStepSize);
			var labelsY = new double[yStepsCount + 1];
			for(int i = 0; i <= yStepsCount; i++)
				labelsY[i] = i * yStepSize;
			return labelsY;
		}

		private static string[] GetXLables(IEnumerable<PlotPoints> ps)
		{
			return ps.SelectRepresentatives(11).Select(p => p.XLabel).ToArray();
		}

		private static string BuildUrl(string[] labelsX, double[] labelsY, double[][] ys, int widthInPixels, int heightInPixels)
		{
			if(labelsY.Length < 2) throw new Exception("bug");
			if(labelsX.Length < 2) throw new Exception("bug");

			double height = labelsY[labelsY.Length - 1];
			var builder = new StringBuilder("http://chart.apis.google.com/chart");
			double yStep = 100.0 / (labelsY.Length - 1);
			double xStep = 100.0 / (labelsX.Length - 1);
			builder.AppendFormat(
				"?cht=lxy&chs={2}x{3}&chco=CC0000,00CC00&chxt=x,y&chg={0},{1}",
				xStep.ToString("F2", NumberFormatInfo.InvariantInfo),
				yStep.ToString("F2", NumberFormatInfo.InvariantInfo),
				widthInPixels,
				heightInPixels
				);
			builder.AppendFormat(
				"&chxl=0:|{0}|1:|{1}&chd=t:{2}",
				string.Join("|", labelsX),
				string.Join("|", labelsY.Select(l => l.ToString()).ToArray()),
				string.Join("|", ys.Select(y => BuildPlotData(y, height)).ToArray())
				);
			return builder.ToString();
		}

		private static string BuildPlotData(double[] ys, double height)
		{
			return "-1|" + string.Join(",", ys.Select(y => FormatDouble(y * 100 / height)).ToArray());
		}

		private static string FormatDouble(double d)
		{
			return d.ToString("F2", NumberFormatInfo.InvariantInfo);
		}

		private static double GetYGridStep(double height)
		{
			double[] mults = { 2.5, 2, 2 };
			double s = 1;
			int i = 0;
			while(height / s > 10)
			{
				s = s * mults[i];
				i = (i + 1) % 3;
			}
			i = 2;
			while(height / s < 4)
			{
				s = s / mults[i];
				i = (i + 2) % 3;
			}
			return s;
		}
	}
}